package com.jamierf.dropwizard.logging.loggly; import ch.qos.logback.classic.LoggerContext; import ch.qos.logback.classic.spi.ILoggingEvent; import ch.qos.logback.contrib.jackson.JacksonJsonFormatter; import ch.qos.logback.contrib.json.classic.JsonLayout; import ch.qos.logback.core.Appender; import ch.qos.logback.core.Layout; import ch.qos.logback.ext.loggly.LogglyAppender; import com.fasterxml.jackson.annotation.JsonProperty; import com.fasterxml.jackson.annotation.JsonTypeName; import com.google.common.base.Optional; import com.google.common.net.HostAndPort; import io.dropwizard.logging.AbstractAppenderFactory; import org.hibernate.validator.constraints.NotEmpty; import org.hibernate.validator.valuehandling.UnwrapValidatedValue; import javax.validation.constraints.NotNull; /** * <p>An {@link io.dropwizard.logging.AppenderFactory} implementation which provides an appender that writes events to Loggly.</p> * <b>Configuration Parameters:</b> * <table summary="Configuration"> * <tr> * <td>Name</td> * <td>Default</td> * <td>Description</td> * </tr> * <tr> * <td>{@code type}</td> * <td><b>REQUIRED</b></td> * <td>The appender type. Must be {@code loggly}.</td> * </tr> * <tr> * <td>{@code threshold}</td> * <td>{@code ALL}</td> * <td>The lowest level of events to write to the server.</td> * </tr> * <tr> * <td>{@code server}</td> * <td>{@code logs-01.loggly.com}</td> * <td>The Loggly server.</td> * </tr> * <tr> * <td>{@code token}</td> * <td><b>REQUIRED</b></td> * <td>Your Loggly customer token.</td> * </tr> * <tr> * <td>{@code tag}</td> * <td>the application name</td> * <td>The Loggly tag.</td> * </tr> * <tr> * <td>{@code logFormat}</td> * <td>the default format</td> * <td> * The Logback pattern with which events will be formatted. See * <a href="http://logback.qos.ch/manual/layouts.html#conversionWord">the Logback documentation</a> * for details. * </td> * </tr> * </table> * * @see io.dropwizard.logging.AbstractAppenderFactory */ @JsonTypeName("loggly") public class LogglyAppenderFactory extends AbstractAppenderFactory { private static final String ISO_8601_FORMAT = "yyyy-MM-dd'T'HH:mm:ss.SSS'Z'"; private static final String ENDPOINT_URL_TEMPLATE = "https://%s/inputs/%s/tag/%s"; @NotNull private HostAndPort server = HostAndPort.fromString("logs-01.loggly.com"); @NotEmpty private String token; @NotNull @UnwrapValidatedValue(false) private Optional<String> tag = Optional.absent(); @JsonProperty public HostAndPort getServer() { return server; } @JsonProperty public void setServer(final HostAndPort server) { this.server = server; } @JsonProperty public String getToken() { return token; } @JsonProperty public void setToken(String token) { this.token = token; } @JsonProperty public Optional<String> getTag() { return tag; } @JsonProperty public void setTag(final Optional<String> tag) { this.tag = tag; } @Override public Appender<ILoggingEvent> build(LoggerContext context, String applicationName, Layout<ILoggingEvent> layout) { final LogglyAppender<ILoggingEvent> appender = new LogglyAppender<>(); final String tagName = tag.or(applicationName); appender.setName("loggly-appender"); appender.setContext(context); appender.setEndpointUrl(String.format(ENDPOINT_URL_TEMPLATE, server, token, tagName)); appender.setLayout(layout == null ? buildLayout(context) : layout); addThresholdFilter(appender, threshold); appender.start(); return wrapAsync(appender); } protected Layout<ILoggingEvent> buildLayout(LoggerContext context) { JsonLayout formatter = new JsonLayout(); formatter.setJsonFormatter(new JacksonJsonFormatter()); formatter.setAppendLineSeparator(true); formatter.setContext(context); formatter.setTimestampFormat(ISO_8601_FORMAT); //as per https://www.loggly.com/docs/automated-parsing/#json formatter.setTimestampFormatTimezoneId("UTC"); formatter.start(); return formatter; } }